fix(core): prefer pwsh.exe over Windows PowerShell 5.1 (#25859)#25900
Conversation
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request improves shell command execution on Windows by prioritizing PowerShell 7 (pwsh.exe) over the legacy Windows PowerShell 5.1. This change resolves long-standing issues with argument quoting and improves compatibility with modern shell features like '&&' and '||' operators, while maintaining backward compatibility for users without PowerShell 7 installed. Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request updates the shell configuration to prefer pwsh.exe on Windows to resolve quoting issues and adds integration tests for verification. It also refactors the resolveExecutable utility to be synchronous and updates the test suite to use vi.stubEnv. Feedback indicates that the switch to synchronous I/O blocks the event loop and violates project conventions, identifies a regression in Windows executable resolution for extensionless files, and suggests using asynchronous operations with real-path resolution for better security and performance.
|
/gemini review |
|
Note this PR is non-breaking by design: Copilot CLI refuses to run without pwsh 7 (#847) and won't support 5.1 (#411, not planned). Here, 5.1 users keep working exactly as before — only users who already installed pwsh get the fix. |
506111a to
650765e
Compare
|
This pull request is being closed as it has been open for 14 days without a 'help wanted' designation. We encourage you to find and contribute to existing 'help wanted' issues in our backlog! Thank you for your understanding. |
…PATH fallback After this PR turned `resolveExecutable` into a synchronous function, a new caller introduced in main (google-gemini#26868) — `await resolveExecutable('rg')` inside `resolveRipgrepPath` — became an `await` on a non-Promise value once main was merged into this branch. That triggers ESLint's `@typescript-eslint/await-thenable` rule and breaks CI lint. Remove the now-redundant `await`. `resolveRipgrepPath` itself stays async because it still has two real `await fileExists(...)` checks on the bundled candidate paths above this call. Behavior is identical; this is purely an API-signature alignment. Also update `ripGrep.test.ts` to use `mockReturnValue` instead of `mockResolvedValue` for `resolveExecutable`, since the mocked function is no longer a Promise.
|
Thanks for the review. The auto-close bot fired again on the PR after you reopened it — same misconfigured policy. The lint failure from the main-merge is fixed and pushed to the branch (PR head won't show it until reopen). The I resolved the main conflict in favor of the sync a) the alternative has a large transitive blast radius — cascading async through the consumers of b) the previous async version was already synchronous in effect — serial c) the surrounding execution flow has large synchronous sections — command parsing ( |
|
@scidomino — the auto-close bot closed the PR again after your reopen (request-review button isn't available while it's closed). Could you reopen and add the 'help wanted' label? Lint fix and rationale for the sync conflict are in the comment above. |
|
✅ 69 tests passed successfully on gemini-3-flash-preview. 🧠 Model Steering GuidanceThis PR modifies files that affect the model's behavior (prompts, tools, or instructions).
This is an automated guidance message triggered by steering logic signatures. |
getShellConfiguration() on Windows scans PATH for pwsh.exe via synchronous fs.accessSync on every call. Calling it from a React render path, even one that runs only at /quit, is undesirable. SessionSummaryDisplay only reads `.shell` for escapeShellArg. getShellConfiguration always returns 'powershell' on Windows (regardless of which executable resolves) and 'bash' on Unix, so the symbolic shell type is fully determined by the platform. Replace the call with `isWindows() ? 'powershell' : 'bash'`. User-visible behavior is unchanged: UUID quoting, malicious-id escaping, and the Windows double-quote footer wrap (google-gemini#26669) all follow the same code paths. Drop the now-unused getShellConfiguration mock from the test setup.

Summary
Fixes #25859. Related: closes-as-effectively-same #18374; addresses the feature-request spirit of #15493, #2353, #6413.
On Windows,
run_shell_commandwith embedded double quotes (e.g.node -e 'console.log(\"test\")') fails because Windows PowerShell 5.1 silently strips\"during native-command argument passing. PowerShell 7 (pwsh.exe) uses standards-compliant argument passing (\$PSNativeCommandArgumentPassing = 'Windows'by default since 7.3) and does not exhibit this behavior.This PR makes
getShellConfigurationpreferpwsh.exefromPATHoverpowershell.exeon Windows. Users with PowerShell 7 installed get the fix; users without keep the existing behavior — no regression either way.Context
Windows PowerShell 5.1 is in legacy maintenance mode. Microsoft itself displays this banner when 5.1 starts:
```
Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.
Install the latest PowerShell for new features and improvements!
https://aka.ms/PSWindows
```
PS 5.1 is .NET Framework-bound, Windows-only, and lags PS 7 on shell behavior. `$PSNativeCommandArgumentPassing` (fixes native-cmd arg passing) and bash-style `&&`/`||` chain operators were added in PS 7 and cannot be backported to 5.1.
Respecting the user's environment. gemini-cli is a developer tool, and developers who work on Windows overwhelmingly install PowerShell 7 — it's the modern, cross-platform, actively-maintained runtime Microsoft recommends. Currently gemini-cli ignores the user's choice and pins Windows invocations to `powershell.exe` (5.1 on any modern Windows) regardless of whether pwsh 7 is installed. This PR follows both Microsoft's own guidance and the user's clear intent: prefer PS 7 when it's on their system, fall back to 5.1 only when it isn't.
Side benefits
PowerShell 7 also addresses other Windows-specific pain reported against gemini-cli:
These aren't explicitly targeted by the fix but improve as a natural consequence for users who have pwsh 7 installed.
Verification
Reproduce directly in each shell — same commands, different results.
Issue 1: embedded double quotes stripped (#25859)
Windows PowerShell 5.1:
```
PS C:\Users\EugenPC> node -e 'console.log("test")'
[eval]:1
console.log(test)
^
ReferenceError: test is not defined
at [eval]:1:13
...
Node.js v22.18.0
```
PowerShell 7.6.1:
```
PS C:\Users\EugenPC> node -e 'console.log("test")'
test
```
Issue 2: `&&` / `||` operators rejected (#20773, #21997, #18022)
Windows PowerShell 5.1:
```
PS C:\Users\EugenPC> echo a && echo b
At line:1 char:8
The token '&&' is not a valid statement separator in this version.
```
PowerShell 7.6.1:
```
PS C:\Users\EugenPC> echo a && echo b
a
b
```
With this PR, gemini-cli routes through `pwsh.exe` on Windows when it's installed, inheriting the correct behavior for both issues.
Behavior matrix
Only the first row changes behavior.
Tests
Root cause
PR #11157 (2025-10-16) changed the default Windows shell from `cmd.exe /d /s /c` to `powershell.exe -NoProfile -Command`. Windows PowerShell 5.1 lacks `$PSNativeCommandArgumentPassing` (introduced in PS 7.2 as experimental, default since 7.3) and has a long-standing runtime limitation: `"` inside arguments is dropped when invoking native executables. Fixable only by running through PS 7 — which is what this PR does.